package com.starsea.book.ui.newpage.weidget.banner

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.TypedValue
import android.view.View
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import androidx.annotation.DrawableRes
import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import java.io.IOException
import java.io.InputStream

/**
 * 描述:
 */
object BannerUtil {
  fun dp2px(context: Context, dpValue: Float): Int {
    return TypedValue.applyDimension(
      TypedValue.COMPLEX_UNIT_DIP,
      dpValue,
      context.resources.displayMetrics
    ).toInt()
  }

  fun sp2px(context: Context, spValue: Float): Int {
    return TypedValue.applyDimension(
      TypedValue.COMPLEX_UNIT_SP,
      spValue,
      context.resources.displayMetrics
    ).toInt()
  }

  fun getItemImageView(
    context: Context,
    @DrawableRes resId: Int,
    localImageSize: LocalImageSize,
    scaleType: ScaleType?
  ): ImageView {
    val imageView = ImageView(context)
    imageView.setImageBitmap(
      getScaledImageFromResource(
        context,
        resId,
        localImageSize.maxWidth,
        localImageSize.maxHeight,
        localImageSize.minWidth,
        localImageSize.minHeight
      )
    )
    imageView.isClickable = true
    imageView.scaleType = scaleType
    return imageView
  }

  fun resetPageTransformer(views: MutableList<View?>?) {
    if (views == null) {
      return
    }
    for (view in views) {
      view!!.visibility = View.VISIBLE
      ViewCompat.setAlpha(view, 1f)
      ViewCompat.setPivotX(view, view.measuredWidth * 0.5f)
      ViewCompat.setPivotY(view, view.measuredHeight * 0.5f)
      ViewCompat.setTranslationX(view, 0f)
      ViewCompat.setTranslationY(view, 0f)
      ViewCompat.setScaleX(view, 1f)
      ViewCompat.setScaleY(view, 1f)
      ViewCompat.setRotationX(view, 0f)
      ViewCompat.setRotationY(view, 0f)
      ViewCompat.setRotation(view, 0f)
    }
  }

  fun isIndexNotOutOfBounds(position: Int, collection: Collection<*>?): Boolean {
    return isCollectionNotEmpty(collection) && position < collection!!.size
  }

  fun isCollectionEmpty(collection: Collection<*>?, vararg args: Collection<*>?): Boolean {
    if (collection == null || collection.isEmpty()) {
      return true
    }
    for (arg in args) {
      if (arg == null || arg.isEmpty()) {
        return true
      }
    }
    return false
  }

  fun isCollectionNotEmpty(collection: Collection<*>?, vararg args: Collection<*>?): Boolean {
    return !isCollectionEmpty(collection, *args)
  }

  /**
   * 从资源获取 Bitmap
   * bitmap 的宽高在 maxWidth maxHeight 和 minWidth minHeight 之间
   */
  fun getScaledImageFromResource(
    context: Context,
    resId: Int,
    maxWidth: Int,
    maxHeight: Int,
    minWidth: Float,
    minHeight: Float
  ): Bitmap? {
    var maxWidth = maxWidth
    var maxHeight = maxHeight
    var result: LoadBitmapPair<Throwable?>
    do {
      result = getImageFromResource(context, resId, maxWidth, maxHeight)
      if (result != null && result.first != null) {
        break
      }
      maxWidth /= 2
      maxHeight /= 2
    } while (result != null && result.second is OutOfMemoryError && maxWidth > minWidth && maxHeight > minHeight)
    return if (result == null) {
      null
    } else {
      result.first
    }
  }

  /**
   * 从资源获取Bitmap
   * 最大宽高为 maxWidth maxHeight
   */
  fun getImageFromResource(
    context: Context,
    resId: Int,
    maxWidth: Int,
    maxHeight: Int
  ): LoadBitmapPair<Throwable?> {
    var result: LoadBitmapPair<Throwable?>
    val decodeOptions = BitmapFactory.Options()
    val preferredConfig = Bitmap.Config.RGB_565
    var `is`: InputStream? = null
    try {
      if (maxWidth == 0 && maxHeight == 0) {
        decodeOptions.inPreferredConfig = preferredConfig
        `is` = context.resources.openRawResource(resId)
        result = LoadBitmapPair(BitmapFactory.decodeStream(`is`, null, decodeOptions), null)
        `is`.close()
      } else {
        // If we have to resize this image, first get the natural
        // bounds.
        decodeOptions.inJustDecodeBounds = true
        decodeOptions.inPreferredConfig = preferredConfig
        `is` = context.resources.openRawResource(resId)
        BitmapFactory.decodeStream(`is`, null, decodeOptions)
        `is`.reset()
        `is`.close()
        val actualWidth = decodeOptions.outWidth
        val actualHeight = decodeOptions.outHeight

        // Then compute the dimensions we would ideally like to decode to.
        val desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight)
        val desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth)

        // Decode to the nearest power of two scaling factor.
        decodeOptions.inJustDecodeBounds = false
        // doesn't
        // support it?
        // decodeOptions.inPreferQualityOverSpeed =
        // PREFER_QUALITY_OVER_SPEED;
        decodeOptions.inSampleSize =
          calculateInSampleSize(decodeOptions, desiredWidth, desiredHeight)
        decodeOptions.inPreferredConfig = preferredConfig
        `is` = context.resources.openRawResource(resId)
        val tempBitmap = BitmapFactory.decodeStream(`is`, null, decodeOptions)
        `is`.close()
        // If necessary, scale down to the maximal acceptable size.
        if (tempBitmap != null && (tempBitmap.width > desiredWidth || tempBitmap.height > desiredHeight)) {
          result = LoadBitmapPair(
            Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true),
            null
          )
          tempBitmap.recycle()
        } else {
          result = LoadBitmapPair(tempBitmap, null)
        }
      }
    } catch (exception: OutOfMemoryError) {
      exception.printStackTrace()
      result = LoadBitmapPair(null, exception)
    } catch (exception: Exception) {
      exception.printStackTrace()
      result = LoadBitmapPair(null, exception)
    } finally {
      if (`is` != null) {
        try {
          `is`.close()
        } catch (e: IOException) {
          e.printStackTrace()
        }
      }
    }
    return result
  }

  fun getResizedDimension(
    maxPrimary: Int,
    maxSecondary: Int,
    actualPrimary: Int,
    actualSecondary: Int
  ): Int {
    // If no dominant value at all, just return the actual.
    if (maxPrimary == 0 && maxSecondary == 0) {
      return actualPrimary
    }

    // If primary is unspecified, scale primary to match secondary's scaling ratio.
    if (maxPrimary == 0) {
      val ratio = maxSecondary.toDouble() / actualSecondary.toDouble()
      return (actualPrimary * ratio).toInt()
    }
    if (maxSecondary == 0) {
      return maxPrimary
    }
    val ratio = actualSecondary.toDouble() / actualPrimary.toDouble()
    var resized = maxPrimary
    if (resized * ratio > maxSecondary) {
      resized = (maxSecondary / ratio).toInt()
    }
    return resized
  }

  fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    if (reqWidth == 0 || reqHeight == 0) {
      return 1
    }

    // Raw height and width of image
    val height = options.outHeight
    val width = options.outWidth
    var inSampleSize = 1
    if (height > reqHeight || width > reqWidth) {
      val halfHeight = height / 2
      val halfWidth = width / 2

      // Calculate the largest inSampleSize value that is a power of 2 and keeps both
      // height and width larger than the requested height and width.
      while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
        inSampleSize *= 2
      }
    }
    return inSampleSize
  }

  class LoadBitmapPair<S : Throwable?> internal constructor(first: Bitmap?, second: S?) :
    Pair<Bitmap?, S>(first, second)
}